<?php declare(strict_types=1);

namespace Zyucv7\Core\Web;

use Zyucv7\Core\Web\Request\Method as RequestMethod;
use Zyucv7\Core\Web\Request\Header as RequestHeader;
use Zyucv7\Core\Web\Request\Uri as RequestUri;

/**
 * HTTP 请求处理类
 * 用于安全地处理和解析HTTP请求数据
 * 与 Response 类配合使用，提供完整的HTTP请求/响应处理
 * 
 * @author Huangkai
 * @version 2.0
 */
class Request
{

    /**
     * 单例实例
     * 
     * @var self|null
     */
    private static $instance = null;
    
    /**
     * 请求方法
     * 
     * @var string
     */
    private $method;
    
    /**
     * 请求URI
     * 
     * @var RequestUri
     */
    private $uri;
    
    /**
     * 查询字符串参数
     * 
     * @var array
     */
    private $query = [];
    
    /**
     * 请求体参数
     * 
     * @var array
     */
    private $body = [];
    
    /**
     * Cookie数据
     * 
     * @var array
     */
    private $cookies = [];
    
    /**
     * 服务器变量
     * 
     * @var array
     */
    private $server = [];
    
    /**
     * 文件上传数据
     * 
     * @var array
     */
    private $files = [];
    
    /**
     * 原始请求体
     * 
     * @var string|null
     */
    private $rawBody = null;
    
    /**
     * 是否已解析请求体
     * 
     * @var bool
     */
    private $parsedBody = false;

    /**
     * 请求头对象
     * 
     * @var RequestHeader
     */
    private $header;
    
    /**
     * 构造函数
     * 
     * @param array|null $get GET参数
     * @param array|null $post POST参数
     * @param array|null $server 服务器变量
     * @param array|null $files 上传文件
     * @param array|null $cookies Cookie数据
     */
    public function __construct(
        ?array $get = null,
        ?array $post = null,
        ?array $server = null,
        ?array $files = null,
        ?array $cookies = null
    ) {
        $this->query = $get ?? $_GET;
        $this->body = $post ?? $_POST;
        $this->server = $server ?? $_SERVER;
        $this->files = $files ?? $_FILES;
        $this->cookies = $cookies ?? $_COOKIE;
        $this->uri = new RequestUri($this->server);
        $this->header = new RequestHeader($this->server);
        $this->method = $this->determineMethod();
    }

    public static function instance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    /**
     * 确定HTTP请求方法
     * 
     * @return string
     */
    private function determineMethod(): string
    {
        $method = $this->server['REQUEST_METHOD'] ?? RequestMethod::GET;
        $method = strtoupper($method);
        // 处理方法重写（通过POST表单或头信息）
        if ($method === RequestMethod::POST) {
            // 检查 _method 隐藏字段
            if (isset($this->body['_method'])) {
                $overrideMethod = strtoupper($this->body['_method']);
                if (in_array($overrideMethod, [RequestMethod::PUT, RequestMethod::PATCH, RequestMethod::DELETE])) {
                    return $overrideMethod;
                }
            }
            
            // 检查 X-HTTP-Method-Override 头
            $isMethodOver = $this->header->get('X-HTTP-Method-Override');
            if ($isMethodOver !== null) {
                $overrideMethod = strtoupper($isMethodOver);
                if (in_array($overrideMethod, [RequestMethod::PUT, RequestMethod::PATCH, RequestMethod::DELETE])) {
                    return $overrideMethod;
                }
            }
        }
        
        return $method;
    }
    
    /**
     * 获取HTTP请求方法
     * 
     * @return string
     */
    public function getMethod(): string
    {
        return $this->method;
    }
    
    /**
     * 检查是否为指定的HTTP方法
     * 
     * @param string $method
     * @return bool
     */
    public function isMethod(string $method): bool
    {
        return strtoupper($this->method) === strtoupper($method);
    }
    
    /**
     * 检查是否为GET请求
     * 
     * @return bool
     */
    public function isGet(): bool
    {
        return $this->isMethod(RequestMethod::GET);
    }
    
    /**
     * 检查是否为POST请求
     * 
     * @return bool
     */
    public function isPost(): bool
    {
        return $this->isMethod(RequestMethod::POST);
    }
    
    /**
     * 检查是否为PUT请求
     * 
     * @return bool
     */
    public function isPut(): bool
    {
        return $this->isMethod(RequestMethod::PUT);
    }
    
    /**
     * 检查是否为PATCH请求
     * 
     * @return bool
     */
    public function isPatch(): bool
    {
        return $this->isMethod(RequestMethod::PATCH);
    }
    
    /**
     * 检查是否为DELETE请求
     * 
     * @return bool
     */
    public function isDelete(): bool
    {
        return $this->isMethod(RequestMethod::DELETE);
    }
    
    /**
     * 获取请求URI
     * 
     * @return string
     */
    public function getUri(): string
    {
        return $this->uri->getUri();
    }
    
    /**
     * 获取请求路径（不包含查询字符串）
     * 
     * @return string
     */
    public function getPath(): string
    {
        return $this->uri->getPath();
    }
    
    /**
     * 获取查询字符串
     * 
     * @return string
     */
    public function getQueryString(): string
    {
        return $this->uri->getQueryString();
    }
    
    /**
     * 设置查询参数
     * 
     * @param string|null $key 参数键名
     * @param mixed $default 默认值
     * @return mixed
     */
    public function setQuery(string $key, $value)
    {
        $this->query[$key] = $value;
    }
    
    /**
     * 获取查询参数
     * 
     * @param string|null $key 参数键名
     * @param mixed $default 默认值
     * @return mixed
     */
    public function getQuery(?string $key = null, $default = null)
    {
        if ($key === null) {
            return $this->query;
        }
        
        return $this->query[$key] ?? $default;
    }
    
    /**
     * 获取请求体参数
     * 
     * @param string|null $key 参数键名
     * @param mixed $default 默认值
     * @return mixed
     */
    public function getBody(?string $key = null, $default = null)
    {
        $this->parseRawBodyIfNeeded();
        
        if ($key === null) {
            return $this->body;
        }
        
        return $this->body[$key] ?? $default;
    }
    
    /**
     * 获取所有输入参数（GET + POST/PUT等）
     * 
     * @param string|null $key 参数键名
     * @param mixed $default 默认值
     * @return mixed
     */
    public function getInput(?string $key = null, $default = null)
    {
        $this->parseRawBodyIfNeeded();
        
        if ($key === null) {
            return array_merge($this->query, $this->body);
        }
        
        // 优先从body获取，然后从query获取
        return $this->body[$key] ?? $this->query[$key] ?? $default;
    }
    
    /**
     * 解析原始请求体（如果需要）
     * 
     * @return void
     */
    private function parseRawBodyIfNeeded(): void
    {
        if ($this->parsedBody) {
            return;
        }
        
        $contentType = $this->header->getContentType();
        
        // 处理JSON请求体
        if (strpos($contentType, 'application/json') !== false) {
            $rawBody = $this->getRawBody();
            if ($rawBody !== '') {
                $jsonData = json_decode($rawBody, true);
                if (json_last_error() === JSON_ERROR_NONE && is_array($jsonData)) {
                    $this->body = array_merge($this->body, $jsonData);
                }
            }
        }
        
        // 处理application/x-www-form-urlencoded（非POST方法）
        elseif (strpos($contentType, 'application/x-www-form-urlencoded') !== false && !$this->isPost()) {
            $rawBody = $this->getRawBody();
            if ($rawBody !== '') {
                parse_str($rawBody, $parsedBody);
                if (is_array($parsedBody)) {
                    $this->body = array_merge($this->body, $parsedBody);
                }
            }
        }
        
        $this->parsedBody = true;
    }
    
    /**
     * 获取原始请求体
     * 
     * @return string
     */
    public function getRawBody(): string
    {
        if ($this->rawBody === null) {
            $this->rawBody = file_get_contents('php://input') ?: '';
        }
        
        return $this->rawBody;
    }
    
    
    
    /**
     * 获取Cookie值
     * 
     * @param string|null $name Cookie名称
     * @param mixed $default 默认值
     * @return mixed
     */
    public function getCookie(?string $name = null, $default = null)
    {
        if ($name === null) {
            return $this->cookies;
        }
        
        return $this->cookies[$name] ?? $default;
    }
    
    /**
     * 获取服务器变量
     * 
     * @param string|null $key 变量键名
     * @param mixed $default 默认值
     * @return mixed
     */
    public function getServer(?string $key = null, $default = null)
    {
        if ($key === null) {
            return $this->server;
        }
        
        return $this->server[$key] ?? $default;
    }
    
    /**
     * 获取上传文件
     * 
     * @param string|null $name 文件字段名称
     * @return mixed
     */
    public function getFile(?string $name = null)
    {
        if ($name === null) {
            return $this->files;
        }
        
        return $this->files[$name] ?? null;
    }
    
    /**
     * 检查是否为AJAX请求
     * 
     * @return bool
     */
    public function isAjax(): bool
    {
        return $this->header->isAjax();
    }
    
    /**
     * 检查是否为JSON请求
     * 
     * @return bool
     */
    public function isJson(): bool
    {
        return $this->header->isJson();
    }
    
    /**
     * 检查是否为安全的HTTP方法（不会修改资源）
     * 
     * @return bool
     */
    public function isSafeMethod(): bool
    {
        return in_array($this->method, [RequestMethod::GET, RequestMethod::HEAD, RequestMethod::OPTIONS]);
    }
    
    /**
     * 检查是否为幂等的HTTP方法
     * 
     * @return bool
     */
    public function isIdempotent(): bool
    {
        return in_array($this->method, [RequestMethod::GET, RequestMethod::HEAD, RequestMethod::PUT, RequestMethod::DELETE, RequestMethod::OPTIONS]);
    }
    
    /**
     * 获取客户端IP地址
     * 
     * @return string
     */
    public function getClientIp(): string
    {
        $ipHeaders = [
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_REAL_IP',
            'HTTP_CLIENT_IP',
            'REMOTE_ADDR'
        ];
        
        foreach ($ipHeaders as $header) {
            if (!empty($this->server[$header])) {
                // 处理可能的多个IP地址（X-Forwarded-For）
                $ips = explode(',', $this->server[$header]);
                $ip = trim($ips[0]);
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    return $ip;
                }
            }
        }
        
        return $this->server['REMOTE_ADDR'] ?? '127.0.0.1';
    }
    
    /**
     * 获取用户代理
     * 
     * @return string
     */
    public function getUserAgent(): string
    {
        return $this->header->getUserAgent();
    }
    
    /**
     * 获取引用页面
     * 
     * @return string
     */
    public function getReferer(): string
    {
        return $this->header->getReferer();
    }
    
    /**
     * 获取请求的MIME类型
     * 
     * @return string
     */
    public function getMimeType(): string
    {
        $contentType = $this->header->getContentType();
        if (strpos($contentType, ';') !== false) {
            return substr($contentType, 0, strpos($contentType, ';'));
        }
        return $contentType;
    }
    
    /**
     * 检查请求是否包含指定的内容类型
     * 
     * @param string $mimeType
     * @return bool
     */
    public function accepts(string $mimeType): bool
    {
        $acceptHeader = $this->header->get('Accept', '*/*');
        $acceptTypes = explode(',', $acceptHeader);
        
        foreach ($acceptTypes as $type) {
            $type = trim($type);
            if ($type === '*/*' || $type === $mimeType || strpos($type, substr($mimeType, 0, strpos($mimeType, '/')) . '/*') === 0) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 创建请求实例（工厂方法）
     * 
     * @return self
     */
    public static function createFromGlobals(): self
    {
        return new self();
    }
    
    /**
     * 获取请求的协议（http/https）
     * 
     * @return string
     */
    public function getScheme(): string
    {
        if (isset($this->server['HTTPS']) && ($this->server['HTTPS'] === 'on' || $this->server['HTTPS'] == 1)) {
            return 'https';
        }
        if (isset($this->server['HTTP_X_FORWARDED_PROTO']) && $this->server['HTTP_X_FORWARDED_PROTO'] === 'https') {
            return 'https';
        }
        return 'http';
    }
}